package DataStorage;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Order;

import HibernateSQLite.java.util.HibernateUtil;

public class GenericDAOHibernate<T, ID extends Serializable, DAOImpl extends IGenericDAO<T, ID>>
		implements IGenericDAO<T, ID> {

	private Class<T> entityClass;
	protected static Session session;
	protected static Session SessionHistory;

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public GenericDAOHibernate() {
		this.entityClass = (Class) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];

	}

	public Session getSession() {
		if (session == null) {
			session = HibernateUtil.getSessionFactory().getCurrentSession();
			session.beginTransaction();
		}
		if (!session.isOpen()) {
			// session = HibernateUtil.getSessionFactory().openSession();
			session = HibernateUtil.getSessionFactory().getCurrentSession();
			session.beginTransaction();
		}

		return session;
	}

	public void commit() {
		session.getTransaction().commit();
	}

	public void closeSession() {
		HibernateUtil.closeSession();
	}

	public Session getSessionHistory() {
		SessionHistory = HibernateUtil.getSessionHistoryFactory()
				.getCurrentSession();
		SessionHistory.beginTransaction();
		return SessionHistory;
	}

	public void commitSessionHistory() {
		SessionHistory.getTransaction().commit();

	}

	public void closeSessionHistory() {
		HibernateUtil.closeHistorySession();
	}

	public Class<T> getEntityClass() {
		return entityClass;
	}

	public void setEntityClass(Class<T> entityClass) {
		this.entityClass = entityClass;
	}

	public int getNumberFiled(T entity) {
		Field[] fields = entity.getClass().getDeclaredFields();
		return fields.length;
	}

	@SuppressWarnings("unchecked")
	public T getEntity(ID id) {
		try {
			return (T) getSession().load(entityClass, id);
		} catch (HibernateException e) {
			e.printStackTrace();
		} finally {
			this.closeSession();
		}
		return null;
	}
	@SuppressWarnings("unchecked")
	public T getEntityFromHistroy(ID id) {
		try {
			return (T) getSessionHistory().load(entityClass, id);
		} catch (HibernateException e) {
			e.printStackTrace();
		} finally {
			this.closeSessionHistory();
		}
		return null;
	}

	@SuppressWarnings("unchecked")
	public T getEntity(long id) {
		try {
			return (T) getSession().load(entityClass, id);
		} catch (HibernateException e) {
			e.printStackTrace();
		} finally {
			this.closeSession();
		}
		return null;
	}
	@SuppressWarnings("unchecked")
	public T getEntityFromHistroy(long id) {
		try {
			return (T) getSessionHistory().load(entityClass, id);
		} catch (HibernateException e) {
			e.printStackTrace();
		} finally {
			this.closeSessionHistory();
		}
		return null;
	}

	@SuppressWarnings("unchecked")
	@Override
	public T getEntityWithLock(ID id, boolean lock) {
		try {
			T entity;
			if (lock)
				entity = (T) getSession().load(getEntityClass(), id,
						LockMode.UPGRADE);
			else
				entity = this.getEntity(id);

			return entity;
		} catch (HibernateException e) {
			e.printStackTrace();
		} finally {
			this.closeSession();
		}
		return null;

	}

	@SuppressWarnings("rawtypes")
	@Override
	public List getAllEntity() {
		return this.getByCriteria();
	}

	@SuppressWarnings("rawtypes")
	@Override
	public List getEntityByExample(T exampleInstance) {
		try {
			Criteria crit = getSession().createCriteria(getEntityClass());
			Example example = Example.create(exampleInstance);
			crit.add(example);
			return crit.list();

		} catch (HibernateException e) {
			e.printStackTrace();
		} finally {
			this.closeSession();
		}
		return null;

	}

	@Override
	public void update(T entity) {
		try {
			Session session = getSession();
			Transaction ts = session.beginTransaction();
			session.update(entity);
			ts.commit();
		} catch (HibernateException e) {
			e.printStackTrace();
		} finally {
			this.closeSession();
		}

	}

	@Override
	public void updateWithLock(T entity, LockMode lock) {
		try {
			Session session = getSession();
			Transaction ts = session.beginTransaction();
			session.load(getEntityClass(), LockMode.UPGRADE);
			ts.commit();
		} catch (HibernateException e) {
			e.printStackTrace();
		} finally {
			this.closeSession();
		}

	}

	@Override
	public void save(T entity) {
		try {
			Session session = getSession();
			Transaction ts = session.beginTransaction();
			session.save(entity);
			ts.commit();
		} catch (HibernateException e) {
			e.printStackTrace();
		} finally {
			this.closeSession();
		}

	}

	@Override
	public void saveOrUpdate(T entity) {
		try {
			Session session = getSession();
			Transaction ts = session.beginTransaction();
			session.saveOrUpdate(entity);
			ts.commit();
		} catch (HibernateException e) {
			e.printStackTrace();
		} finally {
			this.closeSession();
		}

	}

	@Override
	public void saveOrUpAll(Collection<T> enttities) {
		try {
			Session session = getSession();
			Transaction ts = session.beginTransaction();

			session.saveOrUpdate(enttities);
			ts.commit();
		} catch (HibernateException e) {
			e.printStackTrace();
		} finally {
			this.closeSession();
		}

	}

	@Override
	public void delete(T entity) {
		try {
			Session session = getSession();
			Transaction ts = session.beginTransaction();

			session.delete(entity);
			ts.commit();
		} catch (HibernateException e) {
			e.printStackTrace();
		} finally {
			this.closeSession();
		}
	}

	public void deleteFromHistory(T entity) {
		try {
			Session session = getSessionHistory();
			Transaction ts = session.beginTransaction();

			session.delete(entity);
			ts.commit();
		} catch (HibernateException e) {
			e.printStackTrace();
		} finally {
			this.closeSessionHistory();
		}

	}

	@Override
	public void deleteById(ID id) {
		this.delete(this.getEntity(id));

	}
	public void deleteByIdFromHistory(ID id){
		this.deleteFromHistory(this.getEntityFromHistroy(id));
	}

	@Override
	public void deleAll(Collection<T> entities) {
		try {
			Session session = getSession();
			Transaction ts = session.beginTransaction();

			for (T entity : entities) {
				session.delete(entity);
			}
			ts.commit();
		} catch (HibernateException e) {
			e.printStackTrace();
		} finally {
			this.closeSession();
		}

	}

	@Override
	public void deleAllFromHistory(Collection<T> entities) {
		try {
			Session session = getSessionHistory();
			Transaction ts = session.beginTransaction();

			for (T entity : entities) {
				session.delete(entity);
			}
			ts.commit();
		} catch (HibernateException e) {
			e.printStackTrace();
		} finally {
			this.closeSessionHistory();
		}

	}

	@SuppressWarnings("rawtypes")
	protected List getByCriteria(Criterion... criterion) {
		try {
			Criteria crit = getSession().createCriteria(getEntityClass());
			for (Criterion c : criterion) {
				crit.add(c);
			}
			return crit.list();
		} catch (HibernateException e) {
			e.printStackTrace();
		} finally {
			this.closeSession();
		}
		return null;
	}

	@SuppressWarnings("rawtypes")
	public List getByCriteria(Order order, Criterion... criterion) {
		try {
			Criteria crit = getSession().createCriteria(getEntityClass());
			for (Criterion c : criterion) {
				crit.add(c);
			}

			if (order != null)
				crit.addOrder(order);
			return crit.list();
		} catch (HibernateException e) {
			e.printStackTrace();
		} finally {
			this.closeSession();
		}
		return null;

	}

}
